package activity

import (
	"context"
	"encoding/json"
	"fmt"
	"strconv"
	"strings"

	"mall/common/cachex"
	"mall/common/ctxdatax"
	"mall/common/globalkey"
	"mall/common/resultx"
	"mall/service/backend/common/utils/pointy"
	"mall/service/forum/api/internal/lib/userrank"
	"mall/service/forum/api/internal/logic/util"
	"mall/service/forum/api/internal/svc"
	"mall/service/forum/api/internal/types"
	"mall/service/forum/rpc/forumclient"
	"mall/service/user/rpc/userclient"

	"github.com/sirupsen/logrus"
	"github.com/zeromicro/go-zero/core/logx"
)

type GetUserActivityLogic struct {
	logx.Logger
	ctx    context.Context
	svcCtx *svc.ServiceContext
}

func NewGetUserActivityLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetUserActivityLogic {
	return &GetUserActivityLogic{
		Logger: logx.WithContext(ctx),
		ctx:    ctx,
		svcCtx: svcCtx,
	}
}

func (l *GetUserActivityLogic) GetUserActivity(req *types.UserActivityReq) (resp *types.UserActivityInfo, err error) {
	userId := ctxdatax.GetUserId(l.ctx)

	var id uint64
	var preload string
	var eagerLoad string = "user:userMeta"
	if req.UserId != nil && *req.UserId > 0 {
		id = *req.UserId
		preload = "user:Id;userMeta:collectEntryCount,collectEntryStatus,collectEntryItems,postPhotoPublicCount,photoNewStatus"
		preload += ",photoPostPublicItems,reviewPublicCount,reviewNewStatus,reviewPublicItems,"
	} else {
		id = userId
		preload = "user:Id;userMeta:reviewCount,reviewNewStatus,reviewItems,askedQuestionCount,askedQuestionNewStatus,askedQuestionItems"
		preload += ",answeredQuestionCount,answeredQuestionNewStatus,answeredQuestionItems,postPhotoCount,photoNewStatus,photoPostItems"
	}
	res, err := l.svcCtx.UserRpc.GetUserProfile(l.ctx, &userclient.GetProfileReq{
		UserId:     pointy.GetPointer(id),
		Preload:    pointy.GetPointer(preload),
		EagerLoad:  pointy.GetPointer(eagerLoad),
		VisitorId:  pointy.GetPointer(userId),
		PlatformId: pointy.GetPointer(globalkey.ThirdPartyPlatformWechatMini),
	})
	if err != nil {
		return nil, err
	}

	if res == nil || res.UserId == nil {
		return nil, resultx.NewErrCode(resultx.USER_ID_INVALID)
	}

	var reviewCount *uint64
	var reviewPublicCount *uint64
	var reviewNewStatus int32
	var reviewItems []*types.PostInfo
	var reviewPublicItems []*types.PostInfo
	var askedQuestionCount *uint64
	var askedQuestionNewStatus int32
	var askedQuestionItems []*types.TopicInfo
	var answeredQuestionCount *uint64
	var answeredQuestionNewStatus int32
	var answeredQuestionItems []*types.ReplyInfo
	var postPhotoCount *uint64
	var postPhotoPublicCount *uint64
	var photoNewStatus int32
	var photoPostItems []*types.PhotoPostInfo
	var photoPostPublicItems []*types.PhotoPostInfo
	var collectEntryCount *uint64
	var collectEntryStatus int32
	var collectEntryItems []*types.KbEntryInfo
	if len(res.UserMeta) > 0 {
		// logrus.Info(fmt.Sprintf("GetUserActivity res.UserMeta: %+v", res.UserMeta))
		for _, v := range res.UserMeta {
			if v.Key == nil || v.Value == nil {
				continue
			}

			val := strings.TrimSpace(*v.Value)
			if len(val) == 0 {
				continue
			}

			switch strings.TrimSpace(*v.Key) {
			case "reviewCount":
				if i, err := strconv.ParseInt(val, 10, 64); err == nil {
					reviewCount = pointy.GetPointer(uint64(i))
				}
			case "reviewPublicCount":
				if i, err := strconv.ParseInt(val, 10, 64); err == nil {
					reviewPublicCount = pointy.GetPointer(uint64(i))
				}
			case "reviewNewStatus":
				if i, err := strconv.ParseInt(val, 10, 32); err == nil {
					reviewNewStatus = int32(i)
				}
			case "reviewItems":
				if err := json.Unmarshal([]byte(val), &reviewItems); err != nil {
					logx.WithContext(l.ctx).Error("UserActivity reviewItems Unmarshal err : %+v , val : %s ", err, val)
				}
			case "reviewPublicItems":
				if err := json.Unmarshal([]byte(val), &reviewPublicItems); err != nil {
					logx.WithContext(l.ctx).Error("UserActivity reviewPublicItems Unmarshal err : %+v , val : %s ", err, val)
				}
			case "askedQuestionCount":
				if i, err := strconv.ParseInt(val, 10, 64); err == nil {
					askedQuestionCount = pointy.GetPointer(uint64(i))
				}
			case "askedQuestionNewStatus":
				if i, err := strconv.ParseInt(val, 10, 32); err == nil {
					askedQuestionNewStatus = int32(i)
				}
			case "askedQuestionItems":
				if err := json.Unmarshal([]byte(val), &askedQuestionItems); err != nil {
					logx.WithContext(l.ctx).Error("UserActivity askedQuestionItems Unmarshal err : %+v , val : %s ", err, val)
				} else {
					for idx, item := range askedQuestionItems {
						if item.UpdatedAt != nil {
							item.CreatedAt = pointy.GetPointer(util.GetTimeFromNow(*item.UpdatedAt))
						}
						askedQuestionItems[idx] = item
					}
				}
			case "answeredQuestionCount":
				if i, err := strconv.ParseInt(val, 10, 64); err == nil {
					answeredQuestionCount = pointy.GetPointer(uint64(i))
				}
			case "answeredQuestionNewStatus":
				if i, err := strconv.ParseInt(val, 10, 32); err == nil {
					answeredQuestionNewStatus = int32(i)
				}
			case "answeredQuestionItems":
				if err := json.Unmarshal([]byte(val), &answeredQuestionItems); err != nil {
					logx.WithContext(l.ctx).Error("UserActivity answeredQuestionItems Unmarshal err : %+v , val : %s ", err, val)
				} else {
					for idx, item := range answeredQuestionItems {
						if item.UpdatedAt != nil {
							item.CreatedAt = pointy.GetPointer(util.GetTimeFromNow(*item.UpdatedAt))
						}
						answeredQuestionItems[idx] = item
					}
				}
			case "postPhotoCount":
				if i, err := strconv.ParseInt(val, 10, 64); err == nil {
					postPhotoCount = pointy.GetPointer(uint64(i))
				}
			case "postPhotoPublicCount":
				if i, err := strconv.ParseInt(val, 10, 64); err == nil {
					postPhotoPublicCount = pointy.GetPointer(uint64(i))
				}
			case "photoNewStatus":
				if i, err := strconv.ParseInt(val, 10, 32); err == nil {
					photoNewStatus = int32(i)
				}
			case "photoPostItems":
				if err := json.Unmarshal([]byte(val), &photoPostItems); err != nil {
					logx.WithContext(l.ctx).Error("UserActivity photoPostItems Unmarshal err : %+v , val : %s ", err, val)
				}
			case "photoPostPublicItems":
				if err := json.Unmarshal([]byte(val), &photoPostPublicItems); err != nil {
					logx.WithContext(l.ctx).Error("UserActivity photoPostPublicItems Unmarshal err : %+v , val : %s ", err, val)
				}
			case "collectEntryCount":
				if i, err := strconv.ParseInt(val, 10, 64); err == nil {
					collectEntryCount = pointy.GetPointer(uint64(i))
				}
			case "collectEntryStatus":
				if i, err := strconv.ParseInt(val, 10, 32); err == nil {
					collectEntryStatus = int32(i)
				}
			case "collectEntryItems":
				if err := json.Unmarshal([]byte(val), &collectEntryItems); err != nil {
					logx.WithContext(l.ctx).Error("UserProfile collectEntryItems Unmarshal err : %+v , val : %s ", err, val)
				}
			}
		}
	}

	var cacheType uint32
	if reviewNewStatus == -1 { // -1 意味着 user rpc 还没有更新缓存，本次从 forum rpc 查询缓存，并通知user rpc 更新缓存。所以也要显示红点，效果同1。
		cacheType += cachex.ForumNewReview
	}
	if photoNewStatus == -1 {
		cacheType += cachex.ForumNewPhotoPost
	}
	if askedQuestionNewStatus == -1 {
		cacheType += cachex.ForumNewAskedQuestion
	}
	if answeredQuestionNewStatus == -1 {
		cacheType += cachex.ForumNewAnsweredQuestion
	}
	if collectEntryStatus == -1 {
		cacheType += cachex.ForumNewCollectEntry
	}
	if cacheType > 0 {
		ret, err := l.svcCtx.ForumRpc.RefreshCache(l.ctx, &forumclient.RefreshCacheReq{
			UserId:     pointy.GetPointer(id),
			Type:       pointy.GetPointer(cacheType),
			PlatformId: pointy.GetPointer(globalkey.ThirdPartyPlatformWechatMini),
		})
		if err != nil {
			logrus.Info(fmt.Sprintf("GetUserActivity RefreshCache err: %+v", err))
		} else if len(ret.Meta) > 0 {
			for _, v := range ret.Meta {
				if v.Key == nil || v.Value == nil {
					continue
				}

				val := strings.TrimSpace(*v.Value)
				if len(val) == 0 {
					continue
				}

				switch strings.TrimSpace(*v.Key) {
				case "answeredQuestionItems":
					var newQuestionItems []*types.ReplyInfo
					if err := json.Unmarshal([]byte(val), &newQuestionItems); err != nil {
						logx.WithContext(l.ctx).Error("UserActivity answeredQuestionItems Unmarshal err : %+v , val : %s ", err, val)
					} else {
						answeredQuestionItems = newQuestionItems[:]
						for idx, item := range answeredQuestionItems {
							if item.UpdatedAt != nil {
								item.CreatedAt = pointy.GetPointer(util.GetTimeFromNow(*item.UpdatedAt))
							}
							answeredQuestionItems[idx] = item
						}
					}
				case "askedQuestionItems":
					var newQuestionItems []*types.TopicInfo
					if err := json.Unmarshal([]byte(val), &newQuestionItems); err != nil {
						logx.WithContext(l.ctx).Error("UserActivity askedQuestionItems Unmarshal err : %+v , val : %s ", err, val)
					} else {
						askedQuestionItems = newQuestionItems[:]
						for idx, item := range askedQuestionItems {
							if item.UpdatedAt != nil {
								item.CreatedAt = pointy.GetPointer(util.GetTimeFromNow(*item.UpdatedAt))
							}
							askedQuestionItems[idx] = item
						}
					}
				case "photoPostItems":
					var newPhotoItems []*types.PhotoPostInfo
					if err := json.Unmarshal([]byte(val), &newPhotoItems); err != nil {
						logx.WithContext(l.ctx).Error("UserActivity photoPostItems Unmarshal err : %+v , val : %s ", err, val)
					} else {
						photoPostItems = newPhotoItems[:]
					}
				case "photoPostPublicItems":
					var newPhotoItems []*types.PhotoPostInfo
					if err := json.Unmarshal([]byte(val), &newPhotoItems); err != nil {
						logx.WithContext(l.ctx).Error("UserActivity photoPostPublicItems Unmarshal err : %+v , val : %s ", err, val)
					} else {
						photoPostPublicItems = newPhotoItems[:]
					}
				case "reviewItems":
					var newReviewItems []*types.PostInfo
					if err := json.Unmarshal([]byte(val), &newReviewItems); err != nil {
						logx.WithContext(l.ctx).Error("UserActivity reviewItems Unmarshal err : %+v , val : %s ", err, val)
					} else {
						reviewItems = newReviewItems[:]
					}
				case "reviewPublicItems":
					var newReviewItems []*types.PostInfo
					if err := json.Unmarshal([]byte(val), &newReviewItems); err != nil {
						logx.WithContext(l.ctx).Error("UserActivity reviewPublicItems Unmarshal err : %+v , val : %s ", err, val)
					} else {
						reviewPublicItems = newReviewItems[:]
					}
				case "collectEntryItems":
					var newCollectEntryItems []*types.KbEntryInfo
					if err := json.Unmarshal([]byte(val), &newCollectEntryItems); err != nil {
						logx.WithContext(l.ctx).Error("UserActivity collectEntryItems Unmarshal err : %+v , val : %s ", err, val)
					} else {
						collectEntryItems = newCollectEntryItems[:]
					}
				}
			}
		}
	}

	if req.UserId != nil && *req.UserId > 0 {
		userProfile := res.UserProfile

		var birthdayHidden bool
		if userProfile.BirthdayHidden != nil && *userProfile.BirthdayHidden > 0 {
			birthdayHidden = true
		}

		var levelName *string
		var levelUrl *string
		userRankList, err := l.getUserRankList(userId)
		if err != nil {
			logrus.Info(fmt.Sprintf("GetUserActivity getUserRankList err: %+v", err))
		} else {
			var userLevel uint64
			if userProfile.Level != nil && *userProfile.Level > 0 {
				userLevel = uint64(*userProfile.Level)
			}
			for _, v := range userRankList {
				if v.Id != nil && *v.Id == userLevel {
					levelName = v.Name
					levelUrl  = v.MediaUrl
				}
			}
		}

		return &types.UserActivityInfo{
			UserId: res.UserId,
			UserInfo: &types.UserBasicInfo{
				Id:             userProfile.Id,
				Nickname:       userProfile.Nickname,
				Avatar:         userProfile.Avatar,
				Gender:         userProfile.Gender,
				Birthday:       userProfile.Birthday,
				BirthdayHidden: birthdayHidden,
				Level:          userProfile.Level,
				LevelName:      levelName,
				LevelUrl:       levelUrl,
			},
			ReviewCount:       reviewPublicCount,
			ReviewItems:       reviewPublicItems,
			PostPhotoCount:    postPhotoPublicCount,
			PhotoPostItems:    photoPostPublicItems,
			CollectEntryCount: collectEntryCount,
			CollectEntryItems: collectEntryItems,
		}, nil
	}

	return &types.UserActivityInfo{
		UserId:                    res.UserId,
		ReviewCount:               reviewCount,
		ReviewItems:               reviewItems,
		AskedQuestionCount:        askedQuestionCount,
		AskedQuestionItems:        askedQuestionItems,
		AnsweredQuestionCount:     answeredQuestionCount,
		AnsweredQuestionItems:     answeredQuestionItems,
		PostPhotoCount:            postPhotoCount,
		PhotoPostItems:            photoPostItems,
		PhotoNewStatus:            photoNewStatus != 0,
		ReviewNewStatus:           reviewNewStatus != 0,
		AskedQuestionNewStatus:    askedQuestionNewStatus != 0,
		AnsweredQuestionNewStatus: answeredQuestionNewStatus != 0,
	}, nil
}

func (l *GetUserActivityLogic) getUserRankList(userId uint64) (resp []*types.UserRankInfo, err error) {
	userRank := userrank.NewUserRank(l.ctx, l.svcCtx)

	return userRank.List(userId)
}
